﻿//Order.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.BusinessEntities
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Linq;
	using System.Runtime.Serialization;
	using System.Xml.Serialization;

	using Ecng.Common;
	using Ecng.Collections;
	using Ecng.ComponentModel;
	using Ecng.Serialization;

	/// <summary>
	/// Направления заявки.
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	public enum OrderDirections
	{
		/// <summary>
		/// Заявка на покупку.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Покупка")]
		Buy,

		/// <summary>
		/// Заявка на продажу.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Продажа")]
		Sell,
	}

	/// <summary>
	/// Состояния заявки.
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	public enum OrderStates
	{
		/// <summary>
		/// Не отправлена в торговую систему.
		/// </summary>
		/// <remarks>
		/// Первоначальное значение заявки, когда еще программа не отправила транзакцию в торговую систему, или ожидает от биржи подтверждение регистрации.
		/// </remarks>
		[EnumMember]
		[EnumDisplayName("Без состояния")]
		None,

		/// <summary>
		/// Заявка принята биржей и активна.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Активна")]
		Active,

		/// <summary>
		/// Заявка больше не активна на бирже (была полностью удовлетворена или снята из программы).
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Не активна")]
		Done,

		/// <summary>
		/// Заявка не принята торговой системой.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Ошибка")]
		Failed,
	}

	/// <summary>
	/// Типы заявок.
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	public enum OrderTypes
	{
		/// <summary>
		/// Лимитированная.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Лимитированная")]
		Limit,

		/// <summary>
		/// Рыночная.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Рыночная")]
		Market,

		/// <summary>
		/// Условная (стоп, тейк-профик).
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Стоп")]
		Conditional,

		/// <summary>
		/// Заявка на сделку РЕПО.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("РЕПО")]
		Repo,

		/// <summary>
		/// Заявка на модифицированную сделку РЕПО (РЕПО-М).
		/// </summary>
		[EnumMember]
		[EnumDisplayName("РЕПО-М")]
		ExtRepo,

		/// <summary>
		/// Заявка на внебиржевую сделку.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("РПС")]
		Rps,

		/// <summary>
		/// Айсберг-заявка.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Айсберг")]
		Iceberg,

		/// <summary>
		/// Заявка на исполнение поставочных контрактов (например, опционы).
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Исполнение")]
		Execute,
	}

	/// <summary>
	/// Базовое условие заявок (например, параметры стоп- или алго- заявков).
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	[Ignore(FieldName = "IsDisposed")]
	[TypeSchemaFactory(SearchBy.Properties, VisibleScopes.Public)]
	public abstract class OrderCondition : Cloneable<OrderCondition>
	{
		/// <summary>
		/// Инициализировать <see cref="OrderCondition"/>.
		/// </summary>
		protected OrderCondition()
		{
			Parameters = new Dictionary<string, object>();
		}

		/// <summary>
		/// Параметры условия.
		/// </summary>
		public IDictionary<string, object> Parameters { get; set; }

		/// <summary>
		/// Проверить, возможно ли по рыночной цене активировать заявку. И возвратить демо заявки, если это возможно.
		/// </summary>
		/// <param name="depth">Стакан, отражающий текущую рыночную ситуацию.</param>
		/// <returns>Демо заявки.</returns>
		public virtual IEnumerable<Order> TryActivate(MarketDepth depth)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Создать копию условия (копирование параметров условия).
		/// </summary>
		/// <returns>Копия условия.</returns>
		public override OrderCondition Clone()
		{
			var clone = GetType().CreateInstance<OrderCondition>();
			clone.Parameters.AddRange(Parameters.ToArray());
			return clone;
		}
	}

	/// <summary>
	/// Условия исполнения лимитированной заявки.
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	public enum OrderExecutionConditions
	{
		/// <summary>
		/// Поставить в очередь.
		/// </summary>
		[EnumMember]PutInQueue,

		/// <summary>
		/// Немедленно или отклонить.
		/// </summary>
		[EnumMember]MatchOrCancel,

		/// <summary>
		/// Cнять остаток.
		/// </summary>
		[EnumMember]CancelBalance,
	}

	/// <summary>
	/// Системные статусы заявки.
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	public enum OrderStatus : long
	{
		/// <summary>
		/// Транзакция отправлена на сервер.
		/// </summary>
		[EnumMember]SentToServer = 0,

		/// <summary>
		/// Транзакция получена сервером.
		/// </summary>
		[EnumMember]ReceiveByServer = 1,

		/// <summary>
		/// Ошибка отправки транзакции на биржу.
		/// </summary>
		[EnumMember]GateError = 2,

		/// <summary>
		/// Транзакция принята биржей.
		/// </summary>
		[EnumMember]Accepted = 3,

		/// <summary>
		/// Транзакция не принята биржей.
		/// </summary>
		[EnumMember]NotDone = 4,

		/// <summary>
		/// Транзакция не прошла проверку сервера по каким-либо критериям.
		/// Например, проверку на наличие прав у пользователя на отправку транзакции данного типа.
		/// </summary>
		[EnumMember]NotValidated = 5,

		/// <summary>
		/// Транзакция не прошла проверку лимитов сервера.
		/// </summary>
		[EnumMember]NotValidatedLimit = 6,

		/// <summary>
		/// Транзакция клиента, работающего с подтверждением, подтверждена менеджером фирмы.
		/// </summary>
		[EnumMember]AcceptedByManager = 7,

		/// <summary>
		/// Транзакция клиента, работающего с подтверждением, не подтверждена менеджером фирмы.
		/// </summary>
		[EnumMember]NotAcceptedByManager = 8,

		/// <summary>
		/// Транзакция клиента, работающего с подтверждением, снята менеджером фирмы.
		/// </summary>
		[EnumMember]CanceledByManager = 9,

		/// <summary>
		/// Транзакция не поддерживается торговой системой.
		/// </summary>
		[EnumMember]NotSupported = 10,

		/// <summary>
		/// Транзакция не прошла проверку правильности электронной подписи. К примеру, если ключи,
		/// зарегистрированные на сервере, не соответствуют подписи отправленной транзакции.
		/// </summary>
		[EnumMember]NotSigned = 11,

		/// <summary>
		/// Транзакция отправлена на снятие заявки.
		/// </summary>
		[EnumMember]SentToCanceled = 12,

		/// <summary>
		/// Транзакция об успешно снятой заявке.
		/// </summary>
		[EnumMember]Cancelled = 13,

		/// <summary>
		/// Транзакция об успешно исполненной заявке.
		/// </summary>
		[EnumMember]Matched = 14,
	}

	/// <summary>
	/// Заявка.
	/// </summary>
	[System.Runtime.Serialization.DataContract]
	[Serializable]
	[Ignore(FieldName = "IsDisposed")]
	[DisplayName("Заявка")]
	[Description("Информация о заявке.")]
	public class Order : Cloneable<Order>, IExtendableEntity, IAtomic
	{
		private readonly Lazy<TransactionManager> _manager = new Lazy<TransactionManager>(() => new TransactionManager());

		/// <summary>
		/// Создать <see cref="Order"/>.
		/// </summary>
		public Order()
		{
		}

		private TimeSpan _latencyRegistration;

		/// <summary>
		/// Время, которое потребовалось для регистрации заявки.
		/// </summary>
		[TimeSpan]
		[DisplayName("Задержка при регистрации")]
		[Description("Время, которое потребовалось для регистрации заявки.")]
		[Category("Статистика")]
		public TimeSpan LatencyRegistration
		{
			get { return _latencyRegistration; }
			set
			{
				if (_latencyRegistration == value)
					return;

				_latencyRegistration = value;
				Notify("LatencyRegistration");
			}
		}

		private TimeSpan _latencyCancellation;

		/// <summary>
		/// Время, которое потребовалось для отмены заявки.
		/// </summary>
		[TimeSpan]
		[DisplayName("Задержка при отмене")]
		[Description("Время, которое потребовалось для отмены заявки.")]
		[Category("Статистика")]
		public TimeSpan LatencyCancellation
		{
			get { return _latencyCancellation; }
			set
			{
				if (_latencyCancellation == value)
					return;

				_latencyCancellation = value;
				Notify("LatencyCancellation");
			}
		}

		private long _id;

		/// <summary>
		/// Идентификатор заявки.
		/// </summary>
		[Identity]
		[DataMember]
		[DisplayName("Идентификатор")]
		[Description("Идентификатор заявки.")]
		[Category("Основные")]
		public long Id
		{
			get { return _id; }
			set
			{
				if (_id == value)
					return;

				_id = value;
				Notify("Id");
			}
		}

		private DateTime _time;

		/// <summary>
		/// Время выставления заявки на бирже.
		/// </summary>
		[DataMember]
		[DisplayName("Время выставления")]
		[Description("Время выставления заявки на бирже.")]
		[Category("Основные")]
		public DateTime Time
		{
			get { return _time; }
			set
			{
				if (_time == value)
					return;

				_time = value;
				Notify("Time");

				if (LastChangeTime == default(DateTime))
					LastChangeTime = value;
			}
		}

		/// <summary>
		/// Номер транзакции. Автоматически устанавливается при вызове метода <see cref="ITrader.RegisterOrder" />.
		/// </summary>
		[DataMember]
		[DisplayName("Номер транзакции")]
		[Description("Номер транзакции.")]
		[Category("Основные")]
		public long TransactionId { get; set; }

		/// <summary>
		/// Инструмент, по которому выставляется заявка.
		/// </summary>
		[RelationSingle(IdentityType = typeof(string))]
		[DataMember]
		[DisplayName("Инструмент")]
		[Description("Инструмент, по которому выставляется заявка.")]
		[Category("Основные")]
		public Security Security { get; set; }

		private OrderStates _state;

		/// <summary>
		/// Состояние заявки.
		/// </summary>
		[DataMember]
		[DisplayName("Состояние")]
		[Description("Состояние заявки (Активна, Не активна, Ошибка).")]
		[Category("Основные")]
		public OrderStates State
		{
			get { return _state; }
			set
			{
				if (_state == value)
					return;

				_state = value;
				Notify("State");
			}
		}

		/// <summary>
		/// Портфель, в рамках которого торгуется заявка.
		/// </summary>
		[DataMember]
		[RelationSingle(IdentityType = typeof(string))]
		[DisplayName("Портфель")]
		[Description("Портфель, в рамках которого торгуется заявка.")]
		[Category("Основные")]
		public Portfolio Portfolio { get; set; }

		private readonly Lazy<SynchronizedList<string>> _messages = new Lazy<SynchronizedList<string>>(() => new SynchronizedList<string>());

		/// <summary>
		/// Сообщения к заявке (создаются торговой системой при регистрации, изменении или снятии).
		/// </summary>
		[XmlIgnore]
		[Ignore]
		[DisplayName("Сообщения к заявке")]
		[Description("Сообщения к заявке (создаются торговой системой при регистрации, изменении или снятии).")]
		[Category("Основные")]
		public ISynchronizedCollection<string> Messages
		{
			get { return _messages.Value; }
		}

		private DateTime _lastChangeTime;

		/// <summary>
		/// Время последнего изменения заявки (Снятие, Сведение).
		/// </summary>
		[DataMember]
		//[Nullable]
		[DisplayName("Время последнего изменения")]
		[Description("Время последнего изменения заявки (Снятие, Сведение).")]
		[Category("Основные")]
		public DateTime LastChangeTime
		{
			get { return _lastChangeTime; }
			set
			{
				if (_lastChangeTime == value)
					return;

				_lastChangeTime = value;
				Notify("LastChangeTime");
			}
		}

		/// <summary>
		/// Цена заявки.
		/// </summary>
		[DataMember]
		[DisplayName("Цена")]
		[Description("Цена заявки.")]
		[Category("Основные")]
		public decimal Price { get; set; }

		private decimal _volume;

		/// <summary>
		/// Количество контрактов в заявке.
		/// </summary>
		[DataMember]
		[DisplayName("Объем")]
		[Description("Количество контрактов в заявке.")]
		[Category("Основные")]
		public decimal Volume
		{
			get { return _volume; }
			set
			{
				_volume = value;
				VisibleVolume = value;
			}
		}

		/// <summary>
		/// Видимое количество контрактов в заявке. Используется только при <see cref="Type"/> равным <see cref="OrderTypes.Iceberg"/>.
		/// </summary>
		[DataMember]
		[DisplayName("Видимый объем")]
		[Description("Видимое количество контрактов в заявке.")]
		[Category("Основные")]
		public decimal VisibleVolume { get; set; }

		/// <summary>
		/// Направление заявки (покупка или продажа).
		/// </summary>
		[DataMember]
		[DisplayName("Направление")]
		[Description("Направление заявки (покупка или продажа).")]
		[Category("Основные")]
		public OrderDirections Direction { get; set; }

		private decimal _balance;

		/// <summary>
		/// Остаток контрактов в заявке.
		/// </summary>
		[DataMember]
		[DisplayName("Остаток")]
		[Description("Остаток контрактов в заявке.")]
		[Category("Основные")]
		public decimal Balance
		{
			get { return _balance; }
			set
			{
				if (_balance == value)
					return;

				_balance = value;
				Notify("Balance");
			}
		}

		private OrderStatus? _status;

		/// <summary>
		/// Системный статус заявки.
		/// </summary>
		[DataMember]
		[Nullable]
		[Browsable(false)]
		public OrderStatus? Status
		{
			get { return _status; }
			set
			{
				if (_status == value)
					return;

				_status = value;
				Notify("Status");
			}
		}

		private bool _isSystem = true;

		/// <summary>
		/// Является ли заявка системной.
		/// </summary>
		[DataMember]
		[DisplayName("Системная завка")]
		[Description("Является ли заявка системной.")]
		[Category("Основные")]
		public bool IsSystem
		{
			get { return _isSystem; }
			set
			{
				if (_isSystem == value)
					return;

				_isSystem = value;
				Notify("IsSystem");
			}
		}

		/// <summary>
		/// Комментарий к выставляемой заявке.
		/// </summary>
		[DataMember]
		[Primitive]
		[DisplayName("Комментарий")]
		[Description("Комментарий к выставляемой заявке.")]
		[Category("Основные")]
		public string Comment { get; set; }

		/// <summary>
		/// Тип заявки.
		/// </summary>
		[DataMember]
		[DisplayName("Тип заявки")]
		[Description("Тип заявки (Лимитированная, Рыночная, Стоп-заявка и т.д.).")]
		[Category("Основные")]
		public OrderTypes Type { get; set; }
		
		private DateTime? _expiryDate;

		/// <summary>
		/// Время экспирации заявки.
		/// </summary>
		/// <remarks>
		/// Если значение null, то оно не учитывается при регистрации заявки. Если значение равно <see cref="DateTime.Today"/>,
		/// то заявка выставляется сроком на текущую сессию. Если значение равно <see cref="DateTime.MaxValue"/>, то заявка выставляется до отмены.
		/// Иначе, указывается конкретный срок.
		/// </remarks>
		[DataMember]
		[Nullable]
		[DisplayName("Время экспирации заявки")]
		[Description("Время истечения срока действия заявки.")]
		[Category("Основные")]
		public DateTime? ExpiryDate
		{
			get { return _expiryDate; }
			set
			{
				if (_expiryDate == value)
					return;

				_expiryDate = value;
				Notify("ExpiryDate");
			}
		}

		/// <summary>
		/// Условие заявки (например, параметры стоп- или алго- заявков).
		/// </summary>
		//[DataMember]
		//[InnerSchema(IsNullable = true)]
		[Ignore]
		[XmlIgnore]
		[DisplayName("Условие")]
		[Description("Условие заявки (например, параметры стоп- или алго- заявков).")]
		[Category("Условная заявка")]
		public OrderCondition Condition { get; set; }

		/// <summary>
		/// Условие исполнения лимитированной заявки.
		/// </summary>
		[DisplayName("Условие исполнения")]
		[Description("Условие исполнения лимитированной заявки.")]
		[Category("Основные")]
		public OrderExecutionConditions ExecutionCondition { get; set; }

		private Order _derivedOrder;

		/// <summary>
		/// Биржевая заявка, которая была создана стоп-заявкой при активации условия (null, если стоп-условие еще не было активировано).
		/// </summary>
		[DataMember]
		[InnerSchema]
		[Ignore]
		[DisplayName("Связанная заявка")]
		[Description("Биржевая заявка, которая была создана стоп-заявкой при активации условия (пустое значение, если стоп-условие еще не было активировано).")]
		[Category("Условная заявка")]
		public Order DerivedOrder
		{
			get { return _derivedOrder; }
			set
			{
				if (_derivedOrder == value)
					return;

				_derivedOrder = value;
				Notify("DerivedOrderId");
			}
		}

		/// <summary>
		/// Информация для РЕПО\РЕПО-М заявок.
		/// </summary>
		[Ignore]
		[DisplayName("Информация РЕПО\\РЕПО-М")]
		[Description("Информация для РЕПО\\РЕПО-М заявок.")]
		[Category("Основные")]
		public RepoOrderInfo RepoInfo { get; set; }

		/// <summary>
		/// Информация для РПС заявок.
		/// </summary>
		[Ignore]
		[DisplayName("Информация РПС")]
		[Description("Информация для РПС заявок.")]
		[Category("Основные")]
		public RpsOrderInfo RpsInfo { get; set; }

		[field: NonSerialized]
		private SynchronizedDictionary<object, object> _extensionInfo;

		/// <summary>
		/// Расширенная информация по заявке.
		/// </summary>
		/// <remarks>
		/// Необходима в случае хранения в программе дополнительной информации, ассоциированной с заявкой.
		/// Например, время активации, доходность для обычных заявок, или номер заявки-условия для стоп-заявки.
		/// </remarks>
		[Ignore]
		[XmlIgnore]
		[DisplayName("Расширенная информация")]
		[Description("Необходима в случае хранения в программе дополнительной информации, ассоциированной с инструментом.")]
		[Category("Основные")]
		public IDictionary<object, object> ExtensionInfo
		{
			get { return _extensionInfo; }
			set { _extensionInfo = value.Sync(); }
		}

		/// <summary>
		/// true, Если заявка создана из трейс-файла.
		/// </summary>
		[Ignore]
		[DisplayName("Заявка из трейс-файла")]
		[Description("Истина, если заявка создана из трейс-файла.")]
		[Category("Основные")]
		public bool IsTrace { get; set; }

		[field: NonSerialized]
		private ITrader _trader;

		/// <summary>
		/// Шлюз к торговой системе, через который была зарегистрирована данная заявка.
		/// </summary>
		[Ignore]
		[XmlIgnore]
		[DisplayName("Шлюз к торговой системе")]
		[Description("Шлюз к торговой системе, через который была зарегистрирована данная заявка.")]
		[Category("Основные")]
		public ITrader Trader
		{
			get { return _trader; }
			set
			{
				if (_trader == value)
					return;

				_trader = value;

				Notify("Trader");
			}
		}

		[field: NonSerialized]
		private PropertyChangedEventHandler _propertyChanged;

		event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
		{
			add { _propertyChanged += value; }
			remove { _propertyChanged -= value; }
		}

		void INotifyPropertyChangedEx.NotifyPropertyChanged(string info)
		{
			_propertyChanged.SafeInvoke(this, info);
		}

		/// <summary>
		/// Создать копию заявки (копирование параметров заявки).
		/// </summary>
		/// <returns>Копия заявки.</returns>
		public override Order Clone()
		{
			return new Order
			{
				Portfolio = Portfolio,
				Direction = Direction,
				ExecutionCondition = ExecutionCondition,
				Price = Price,
				Security = Security,
				Type = Type,
				Volume = Volume,
				Condition = Condition.CloneNullable(),
				Comment = Comment,
				RepoInfo = RepoInfo
			};
		}

		/// <summary>
		/// Получить строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return "{0}/{1} {2} Цена={3} Объем={4} Сост={5} Бал={6}".Put(TransactionId, Id, Direction == OrderDirections.Buy ? "Покупка" : "Продажа", Price, Volume, State, Balance);
		}

		/// <summary>
		/// Начать обновление данных. Окончание обновление данных инициируется через <see cref="UpdateTransaction.Dispose"/>.
		/// </summary>
		/// <returns>Транзакция на изменения данных.</returns>
		public UpdateTransaction BeginUpdate()
		{
			return _manager.Value.BeginUpdate();
		}

		/// <summary>
		/// Начать чтение данных. В период действия транзакции потоки данных не могут изменить внутреннее состояние объекта, из которого читаются данные.
		/// Окончание чтения данных инициируется через <see cref="ReadTransaction.Dispose"/>.
		/// </summary>
		/// <param name="isUpgradable">Можно ли вызвать внутри блокировки на чтение внутреннюю блокировку на запись.</param>
		/// <returns>Транзакция на чтение данных.</returns>
		public ReadTransaction BeginRead(bool isUpgradable = false)
		{
			return _manager.Value.BeginRead(isUpgradable);
		}

		private void Notify(string propName)
		{
			if (_manager.IsValueCreated)
				_manager.Value.Do(() => NotifyPropertyChangedExHelper.Notify(this, propName));
			else
				NotifyPropertyChangedExHelper.Notify(this, propName);
		}
	}
}
